<!DOCTYPE html>
<meta charset="utf-8">
<title>IEEE754</title>
<pre id="log"></pre>
<script>

function print(msg) {
  document.getElementById('log').innerHTML += msg + '\n';
}

window.onload = function() {
  // prepare
  var _bit2num = {};
  for (var i = 0; i < 0x100; ++i) {
    _bit2num[("0000000" + i.toString(2)).slice(-8)] = i;
  }
  var _IEEE754positive = /^.(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})$/;
  var _IEEE754negative =  /^(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})$/;

  var input = [];
  for (var i = 1; i < 100000; i += 2) {
    input[i - 1] = i / Math.PI;
    input[i] = - i / Math.PI;
  }

  function test1(input) {
    var rv = [], mix, hash, sign, exp, frac, ary, _pooledArray;

    for (var i = 0, l = input.length; i < l; i++) {
      mix = input[i];

      hash = _bit2num;
      sign = mix < 0;
      sign && (mix *= -1);

      // add offset 1023 to ensure positive
      // 0.6931471805599453 = Math.LN2;
      exp  = ((Math.log(mix) / 0.6931471805599453) + 1023) | 0;

      // shift 52 - (exp - 1023) bits to make integer part exactly 53 bits,
      // then throw away trash less than decimal point
      frac = (Math.floor(mix * Math.pow(2, 52 + 1023 - exp))).
              toString(2).slice(1);

      // exp is between 1 and 2047. make it 11 bits
      // http://d.hatena.ne.jp/uupaa/20101128
      _pooledArray = !sign ? _IEEE754positive.exec((exp + 4096).toString(2) + frac)
                           : _IEEE754negative.exec((exp + 2048).toString(2) + frac);
      ary = _pooledArray; // alias
      rv.push(0xcb, hash[ary[1]], hash[ary[2]],
                    hash[ary[3]], hash[ary[4]],
                    hash[ary[5]], hash[ary[6]],
                    hash[ary[7]], hash[ary[8]]);
    }

    return rv;
  }


  function test2(input) {
    var rv = [], mix, sign, exp, frac, upper, r1, r2, r3, r4, r5 ,r6, r7, r8;
    for (var i = 0, l = input.length; i < l; i++) {
      mix = input[i];

      sign = mix < 0;
      sign && (mix *= -1);

      // add offset 1023 to ensure positive
      // 0.6931471805599453 = Math.LN2;
      exp  = ((Math.log(mix) / 0.6931471805599453) + 1023) | 0;

      // shift 52 - (exp - 1023) bits to make integer part exactly 53 bits,
      // then throw away trash less than decimal point
      frac = (Math.floor(mix * Math.pow(2, 52 + 1023 - exp))).toString(2);

      // 13 bits = "1" (no meaning) + 1 bit (sign) + 11 bits (exp)
      // 6144 = 2048 + 4096 = 1100000000000 (2)
      upper = (exp + (sign ? 6144 : 4096)).toString(2);

      // charCode for '0' => 48, '1' => 49

      r8 = ((frac.charCodeAt(52) - 48)     ) + ((frac.charCodeAt(51) - 48) << 1) +
           ((frac.charCodeAt(50) - 48) << 2) + ((frac.charCodeAt(49) - 48) << 3) +
           ((frac.charCodeAt(48) - 48) << 4) + ((frac.charCodeAt(47) - 48) << 5) +
           ((frac.charCodeAt(46) - 48) << 6) + ((frac.charCodeAt(45) - 48) << 7) ;

      r7 = ((frac.charCodeAt(44) - 48)     ) + ((frac.charCodeAt(43) - 48) << 1) +
           ((frac.charCodeAt(42) - 48) << 2) + ((frac.charCodeAt(41) - 48) << 3) +
           ((frac.charCodeAt(40) - 48) << 4) + ((frac.charCodeAt(39) - 48) << 5) +
           ((frac.charCodeAt(38) - 48) << 6) + ((frac.charCodeAt(37) - 48) << 7) ;

      r6 = ((frac.charCodeAt(36) - 48)     ) + ((frac.charCodeAt(35) - 48) << 1) +
           ((frac.charCodeAt(34) - 48) << 2) + ((frac.charCodeAt(33) - 48) << 3) +
           ((frac.charCodeAt(32) - 48) << 4) + ((frac.charCodeAt(31) - 48) << 5) +
           ((frac.charCodeAt(30) - 48) << 6) + ((frac.charCodeAt(29) - 48) << 7) ;

      r5 = ((frac.charCodeAt(28) - 48)     ) + ((frac.charCodeAt(27) - 48) << 1) +
           ((frac.charCodeAt(26) - 48) << 2) + ((frac.charCodeAt(25) - 48) << 3) +
           ((frac.charCodeAt(24) - 48) << 4) + ((frac.charCodeAt(23) - 48) << 5) +
           ((frac.charCodeAt(22) - 48) << 6) + ((frac.charCodeAt(21) - 48) << 7) ;

      r4 = ((frac.charCodeAt(20) - 48)     ) + ((frac.charCodeAt(19) - 48) << 1) +
           ((frac.charCodeAt(18) - 48) << 2) + ((frac.charCodeAt(17) - 48) << 3) +
           ((frac.charCodeAt(16) - 48) << 4) + ((frac.charCodeAt(15) - 48) << 5) +
           ((frac.charCodeAt(14) - 48) << 6) + ((frac.charCodeAt(13) - 48) << 7) ;

      r3 = ((frac.charCodeAt(12) - 48)     ) + ((frac.charCodeAt(11) - 48) << 1) +
           ((frac.charCodeAt(10) - 48) << 2) + ((frac.charCodeAt( 9) - 48) << 3) +
           ((frac.charCodeAt( 8) - 48) << 4) + ((frac.charCodeAt( 7) - 48) << 5) +
           ((frac.charCodeAt( 6) - 48) << 6) + ((frac.charCodeAt( 5) - 48) << 7) ;

      r2 = ((frac.charCodeAt( 4) - 48)     ) + ((frac.charCodeAt( 3) - 48) << 1) +
           ((frac.charCodeAt( 2) - 48) << 2) + ((frac.charCodeAt( 1) - 48) << 3) +
           ((upper.charCodeAt(12) - 48) << 4) + ((upper.charCodeAt(11) - 48) << 5) +
           ((upper.charCodeAt(10) - 48) << 6) + ((upper.charCodeAt( 9) - 48) << 7) ;

      r1 = ((upper.charCodeAt( 8) - 48)     ) + ((upper.charCodeAt( 7) - 48) << 1) +
           ((upper.charCodeAt( 6) - 48) << 2) + ((upper.charCodeAt( 5) - 48) << 3) +
           ((upper.charCodeAt( 4) - 48) << 4) + ((upper.charCodeAt( 3) - 48) << 5) +
           ((upper.charCodeAt( 2) - 48) << 6) + ((upper.charCodeAt( 1) - 48) << 7) ;

      rv.push(0xcb, r1, r2, r3, r4, r5, r6, r7, r8);
    }

    return rv;
  }

  function test3(input) {
    var rv = [], mix, sign, exp, frac, r1, r2, r3, r4, r5 ,r6, r7, r8;
    for (var i = 0, l = input.length; i < l; i++) {
      mix = input[i];

      sign = mix < 0;
      sign && (mix *= -1);

      // exp => 11 bits
      // add offset 1023 to ensure positive
      // 0.6931471805599453 = Math.LN2;
      exp  = ((Math.log(mix) / 0.6931471805599453) + 1023) | 0;

      // frac => 53 bits
      // shift 52 - (exp - 1023) bits to make integer part exactly 53 bits,
      // then throw away trash less than decimal point
      frac = Math.floor(mix * Math.pow(2, 52 + 1023 - exp));

      r8 = frac % 256;
      frac = (frac - r8) / 256; // frac is 45 bits

      r7 = frac % 256;
      frac = (frac - r7) / 256; // frac is 37 bits

      r6 = frac % 256;
      frac = (frac - r6) / 256; // frac is 29 bits

      r5 = frac & 255;
      frac = frac >> 8; // frac is 21 bits

      r4 = frac & 255;
      frac = frac >> 8; // frac is 13 bits

      r3 = frac & 255;
      frac = frac >> 8; // frac is 5 bits

      // take the rest 4 bits out of 5 from frac, and 4 bits from the exp
      r2 = (frac & 15) + ((exp & 15) << 4);

      // take the rest 7 bits of exp, then add sign bit
      r1 = exp >> 4;
      sign && (r1 += 128);

      rv.push(0xcb, r1, r2, r3, r4, r5, r6, r7, r8);
    }

    return rv;
  }

  function test4(input) {
    var rv = [], mix, sign, exp, frac, high, low;

    for (var i = 0, l = input.length; i < l; i++) {
      mix = input[i];

      sign = mix < 0;
      sign && (mix *= -1);

      // exp => 11 bits
      // add offset 1023 to ensure positive
      // 0.6931471805599453 = Math.LN2;
      exp  = ((Math.log(mix) / 0.6931471805599453) + 1023) | 0;

      // frac => 53 bits
      // shift 52 - (exp - 1023) bits to make integer part exactly 53 bits,
      // then throw away trash less than decimal point
      frac = Math.floor(mix * Math.pow(2, 52 + 1023 - exp));

      high = Math.floor(frac / 0x100000000) | exp << 20;
      low  = frac & (0x100000000 - 1);

      sign && (high += 0x80000000);

      rv.push(0xcb, (high >> 24) & 0xff, (high >> 16) & 0xff,
                    (high >>  8) & 0xff,  high        & 0xff,
                    (low  >> 24) & 0xff, (low  >> 16) & 0xff,
                    (low  >>  8) & 0xff,  low         & 0xff);
    }

    return rv;
  }
  function test5(input) {
    var rv = [], mix = 0, sign = false, exp = 0, frac = 0, high = 0, low = 0;

    for (var i = 0, l = input.length; i < l; i++) {
      mix = input[i];

                sign = mix < 0;
                sign && (mix *= -1);

                // exp => 11 bits
                // add offset 1023 to ensure positive
                // 0.6931471805599453 = Math.LN2;
                exp  = ((Math.log(mix) / Math.LN2) + 1023) | 0;

                // frac => 53 bits
                // shift 52 - (exp - 1023) bits to make integer part exactly 53 bits,
                // then throw away trash less than decimal point
                frac = Math.floor(mix * Math.pow(2, 52 + 1023 - exp));

                //  S+-Exp(11)--++-----------------Fraction(52bits)-----------------------+
                //  ||          ||                                                        |
                //  v+----------++--------------------------------------------------------+
                //  00000000|00000000|00000000|00000000|00000000|00000000|00000000|00000000
                //  6      5    55  4        4        3        2        1        8        0
                //  3      6    21  8        0        2        4        6
                //
                //  +----------high(32bits)-----------+ +----------low(32bits)------------+
                //  |                                 | |                                 |
                //  +---------------------------------+ +---------------------------------+
                //  3      2    21  1        8        0
                //  1      4    09  6
                low  = frac & 0xffffffff;
                sign && (exp |= 0x800);
                high = ((frac / 0x100000000) & 0xfffff) | (exp << 20);

                rv.push(0xcb, (high >> 24) & 0xff, (high >> 16) & 0xff,
                              (high >>  8) & 0xff,  high        & 0xff,
                              (low  >> 24) & 0xff, (low  >> 16) & 0xff,
                              (low  >>  8) & 0xff,  low         & 0xff);
    }

    return rv;
  }

  function test6(input) {
    var rv = [], mix = 0, sign = false, exp = 0, frac = 0, high = 0, low = 0,
        log = Math.log, LN2 = Math.LN2,
        floor = Math.floor, pow = Math.pow;

    for (var i = 0, l = input.length; i < l; i++) {
      mix = input[i];

                sign = mix < 0;
                sign && (mix *= -1);

                // exp => 11 bits
                // add offset 1023 to ensure positive
                // 0.6931471805599453 = Math.LN2;
                exp  = ((log(mix) / LN2) + 1023) | 0;

                // frac => 53 bits
                // shift 52 - (exp - 1023) bits to make integer part exactly 53 bits,
                // then throw away trash less than decimal point
//                frac = floor(mix * pow(2, 52 + 1023 - exp));
                frac = mix * pow(2, 52 + 1023 - exp);

                //  S+-Exp(11)--++-----------------Fraction(52bits)-----------------------+
                //  ||          ||                                                        |
                //  v+----------++--------------------------------------------------------+
                //  00000000|00000000|00000000|00000000|00000000|00000000|00000000|00000000
                //  6      5    55  4        4        3        2        1        8        0
                //  3      6    21  8        0        2        4        6
                //
                //  +----------high(32bits)-----------+ +----------low(32bits)------------+
                //  |                                 | |                                 |
                //  +---------------------------------+ +---------------------------------+
                //  3      2    21  1        8        0
                //  1      4    09  6
                low  = frac & 0xffffffff;
                sign && (exp |= 0x800);
                high = ((frac / 0x100000000) & 0xfffff) | (exp << 20);

                rv.push(0xcb, (high >> 24) & 0xff, (high >> 16) & 0xff,
                              (high >>  8) & 0xff,  high        & 0xff,
                              (low  >> 24) & 0xff, (low  >> 16) & 0xff,
                              (low  >>  8) & 0xff,  low         & 0xff);
    }

    return rv;
  }


  var t = new Date;
  test1(input); test1(input);
  var rv = test1(input);
  print('test1 took ' + ((new Date - t) / 3 | 0) + ' ms, results : ' + rv.slice(0, 10) + ' ...');

  var t = new Date;
  test2(input); test2(input);
  var rv = test2(input);
  print('test2 took ' + ((new Date - t) / 3 | 0) + ' ms, results : ' + rv.slice(0, 10) + ' ...');

  var t = new Date;
  test3(input); test3(input);
  var rv = test3(input);
  print('test3 took ' + ((new Date - t) / 3 | 0) + ' ms, results : ' + rv.slice(0, 10) + ' ...');

  var t = new Date;
  test4(input); test4(input);
  var rv = test4(input);
  print('test4 took ' + ((new Date - t) / 3 | 0) + ' ms, results : ' + rv.slice(0, 10) + ' ...');

  var t = new Date;
  test5(input); test5(input);
  var rv = test5(input);
  print('test5 took ' + ((new Date - t) / 3 | 0) + ' ms, results : ' + rv.slice(0, 10) + ' ...');

  var t = new Date;
  test6(input); test6(input);
  var rv = test6(input);
  print('test6 took ' + ((new Date - t) / 3 | 0) + ' ms, results : ' + rv.slice(0, 10) + ' ...');



};
</script>
